use syn::{Attribute, DataEnum, Ident};
use quote::quote;
use syn::Error;
use syn::parse::Parse;
use crate::utils;
use crate::attr::ResponseAttr;

pub fn derive_responder(data_enum: DataEnum, enum_ident: Ident, _attr: Vec<Attribute>) -> proc_macro2::TokenStream {
    let aqua = utils::get_aqua_path();

   // Foreach variant
   let mut status_code_codes = Vec::new();
   let mut content_type_codes = Vec::new();

   for variant in data_enum.variants.iter() {
       let var_ident = &variant.ident;

       let mut status_code = 200u16;
       let mut content_type = quote! { #aqua::http::HttpContentType::ApplicationJson };

       // Parse #[response(...)] for each variant
       for attr in &variant.attrs {
           if !attr.path().is_ident("response") {
               continue;
           }
           if let Ok(parsed) = attr.parse_args_with(ResponseAttr::parse) {
                if let Err(e) = parsed.extract_filed(&mut status_code, &mut content_type) {
                    return e;
                }
           }
       }

       match &variant.fields {
           syn::Fields::Unnamed(fields_unnamed) if fields_unnamed.unnamed.len() == 1 => {
               status_code_codes.push(quote! {
                   Self::#var_ident(_) => #status_code
               });

               content_type_codes.push(quote! {
                   Self::#var_ident(_) => #content_type
               });
           }
           syn::Fields::Unit => {
               status_code_codes.push(quote! {
                   Self::#var_ident => #status_code
               });

               content_type_codes.push(quote! {
                   Self::#var_ident => #content_type
               });
           }
           _ => {
                return Error::new_spanned(
                    &variant.fields,
                    "Only support unit or single-field tuple variants"
                )
                .to_compile_error();
           }
       }
   }

   let expanded = quote! {
       impl #aqua::convert::IntoResponse for #enum_ident {
           fn status_code(&self) -> u16 {
               match self {
                   #( #status_code_codes ),*
               }
           }

           fn content_type(&self) -> #aqua::http::HttpContentType {
               match self {
                   #( #content_type_codes ),*
               }
           }

           fn into_body(self) -> Vec<u8> {
               serde_json::to_vec(&self).unwrap()
           }
       }
   };

   expanded.into()
}